/* Gyroe 1.0.0 */
/* fe_emit.c : Generate the required Intermediate
   Level (IL) opcode required by do_exec. */

#include <stdlib.h>
#include "fe_shared.h"
#include "fe_emit.h"

void init_emit()
{
    cg_stack = malloc(32 * sizeof(int));
    cg_stack_extra = 31; cg_stack_pos = 0;

    assignable = FALSE;
    previous_op = -1;
}

void cg_push(int x)
{
    cg_stack_extra--; cg_stack_pos++;
    if (cg_stack_extra == -1) {
        cg_stack = (int *)realloc(cg_stack, (cg_stack_pos+32) * sizeof (int));
        cg_stack_extra = 31;
    }

    cg_stack[cg_stack_pos] = x;
}

void keep_temp(int a)
{
    if (symtab[a][S_MODE] == M_TEMP)
        symtab[a][S_SCOPE] = SC_IN_USE;
}

void free_temp(int a)
{
    if (symtab[a][S_MODE] == M_TEMP)
        symtab[a][S_SCOPE] = SC_FREE;

}

void integer_temp(int a)
{
    if (symtab[a][S_MODE] == M_TEMP)
        symtab[a][S_USAGE] = T_INTEGER;
}

boolean is_integer(int sym)
{
    int pt, t;

    switch (symtab[sym][S_MODE]) {
        case M_NORMAL:
            t = symtab[sym][S_VTYPE];

            if (t == INTEGER_TYPE)
                return TRUE;

            if (t) {
                pt = symtab[t][S_NEXT];

                if (pt && symtab[pt][S_VTYPE] == INTEGER_TYPE)
                    return TRUE; /* It's a type with an integer parameter. */
            }
            break;
        case M_CONSTANT:
            if (IS_ATOM_INT(symtab[sym][S_OBJ]))
                return TRUE;
            break;
        case M_TEMP:
            if (symtab[sym][S_USAGE] == T_INTEGER)
                return TRUE;
            break;
    }

    return FALSE;
}

void emit(int x)
{
    int i;

    seg_extra--; seg_pos++;
    if (seg_extra == -1) {
        code_area[code_area_pos] = (int *)realloc(code_area[code_area_pos], (seg_pos+128) * sizeof (int));
        seg_extra = 127;
        for (i = seg_pos+1;i < seg_pos+128;i++)
            code_area[code_area_pos][i] = -2;
    }

    code_area[code_area_pos][seg_pos] = x;
}

void emit_opnd(int x)
{
    cg_push(x);
    previous_op = -1;
}

void cont11ii(int op, boolean ii)
{
    int c, source, t;

    emit(op);
    source = cg_stack[cg_stack_pos];
    emit(source);

    c = new_temp_sym();
    t = op_result[op];

    if (t == T_INTEGER || ii && is_integer(source))
        integer_temp(c);

    cg_stack[cg_stack_pos] = c;
    emit(c);

    assignable = TRUE;
}

void cont21d(int op, int a, int b, boolean ii)
{
    int c, t;

    t = op_result[op];
    c = new_temp_sym();

    if (t == T_INTEGER || ii && is_integer(a) && is_integer(b))
        integer_temp(c);

    cg_push(c);
    emit(c);

    assignable = TRUE;
}

void cont21ii(int op, boolean ii)
{
    int a, b;

    b = cg_stack[cg_stack_pos];
    a = cg_stack[cg_stack_pos - 1];

    emit(op);
    emit(a);
    emit(b);

    cg_stack_extra += 2; cg_stack_pos -= 2;

    cont21d(op, a, b, ii);

    previous_op = op;
}

void emit_assign()
{
    int op = 18;
    int source, target;

    source = cg_stack[cg_stack_pos];
    target = cg_stack[cg_stack_pos - 1];

    cg_stack_extra += 2; cg_stack_pos -= 2;

    if (assignable) {
        /* Drop previous code target... */
        seg_extra++; seg_pos--;

        /* ...but keep the previous op. */
        op = previous_op;

        if (is_integer(target)) {
            switch (previous_op) {
                case RHS_SUBS:
                    op = RHS_SUBS_I;

                    BACKPATCH(seg_pos - 2, RHS_SUBS_I);
                    break;
                case PLUS1:
                    op = PLUS1_I;

                    BACKPATCH(seg_pos - 2, PLUS1_I);
                    break;
                case PLUS:
                case MINUS:
                    if (is_integer(code_area[code_area_pos][seg_pos]) &&
                        is_integer(code_area[code_area_pos][seg_pos - 1])) {
                        if (previous_op == PLUS)
                            op = PLUS_I;
                        else
                            op = MINUS_I;

                        BACKPATCH(seg_pos - 2, op);
                    }
                    break;
                default:
                    if (is_integer(source))
                        op = ASSIGN_I; /* Fake to avoid subsequent check */

                    break;
            }
        }
    }
    else {
        if (is_integer(source) && is_integer(target))
            op = ASSIGN_I;

        if (symtab[source][S_MODE] == M_CONSTANT && symtab[target][S_MODE] == M_CONSTANT)
            symtab[target][S_OBJ] = symtab[source][S_OBJ];

        emit(op);
        emit(source);
    }

    assignable = FALSE;
    emit(target);
    previous_op = op;
}

void emit_assign_op_slice(int op)
{
    int a, b, c, d;

    emit(op);

    a = cg_stack[cg_stack_pos];
    b = cg_stack[cg_stack_pos - 1];
    c = cg_stack[cg_stack_pos - 2];
    d = cg_stack[cg_stack_pos - 3];

    emit(d);
    emit(c);
    emit(b);

    c = new_temp_sym();
    cg_stack[cg_stack_pos] = c;

    emit(c); /* place to store result */
    cg_push(a);

    assignable = FALSE;
    previous_op = op;
}

void emit_assign_slice(int op)
{
    emit(op);

    emit(cg_stack[cg_stack_pos - 3]);
    emit(cg_stack[cg_stack_pos - 2]);
    emit(cg_stack[cg_stack_pos - 1]);
    emit(cg_stack[cg_stack_pos]);

    cg_stack_extra += 4; cg_stack_pos -= 4; 

    assignable = FALSE;
    previous_op = op;
}

void emit_assign_subs()
{
    int a, b, c, op;

    a = cg_stack[cg_stack_pos];
    b = cg_stack[cg_stack_pos - 1];
    c = cg_stack[cg_stack_pos - 2];

    cg_stack_extra += 3; cg_stack_pos -= 3;

    if (previous_op != LHS_SUBS && symtab[c][S_MODE] != M_NORMAL ||
        symtab[c][S_VTYPE] != SEQUENCE_TYPE &&
        symtab[symtab[symtab[c][S_VTYPE]][S_NEXT]][S_VTYPE] != SEQUENCE_TYPE)
        op = ASSIGN_SUBS_CHECK;
    else if (is_integer(a))
        op = ASSIGN_SUBS_I;
    else
        op = ASSIGN_SUBS;

    emit(op);
    emit(c); /* sequence */
    emit(b); /* subscript */
    emit(a); /* rhs value */

    assignable = FALSE;
    previous_op = op;
}

void emit_atom_check()
{
    int a;

    if (previous_op == ASSIGN) {
        a = code_area[code_area_pos][seg_pos - 1];

        if (symtab[a][S_MODE] != M_CONSTANT || !IS_ATOM(symtab[a][S_OBJ])) {
            emit(ATOM_CHECK);
            emit(op_info1);
        }
    }
    else if (previous_op == -1 || op_result[previous_op] != T_INTEGER && op_result[previous_op] != T_ATOM) {
        emit(ATOM_CHECK);
        emit(op_info1);
    }

    assignable = FALSE;
    previous_op = ATOM_CHECK;
}

void emit_c_func()
/* Special case : Do cont21ii and cont21d here since C_FUNC needs an extra emit.
   This saves everything else which calls cont21ii or cont21d from an extra if. */
{
    int a, b, c;

    emit(C_FUNC);
    emit(cg_stack[cg_stack_pos - 1]);
    emit(cg_stack[cg_stack_pos]);

    emit(currentsub);
    c = new_temp_sym();
    cg_stack[cg_stack_pos - 1] = c;

    cg_stack_extra++; cg_stack_pos--;

    emit(c);

    assignable = TRUE;
    previous_op = C_FUNC;
}

void emit_call_func()
{
    int a;

    emit(CALL_FUNC);
    emit(cg_stack[cg_stack_pos - 1]);
    emit(cg_stack[cg_stack_pos]);

    a = new_temp_sym();
    cg_stack[cg_stack_pos - 1] = a;
    emit(a);

    cg_stack_extra++; cg_stack_pos--;

    assignable = TRUE;
}

void emit_concat_n()
{
    int c, i, n;

    n = op_info1; /* number of items to concatenate */

    emit(CONCAT_N);
    emit(n);

    for (i = 0;i < n;i++)
        emit(cg_stack[cg_stack_pos - i]); /* reverse order */

    cg_stack_pos -= n; cg_stack_extra += n;
    /* Cut the stuff that was to be concatenated. */

    c = new_temp_sym();
    emit(c);
    cg_push(c);

    assignable = TRUE;
    previous_op = CONCAT_N;
}

void emit_divide()
{
    int a;

    a = cg_stack[cg_stack_pos];

    if (symtab[a][S_MODE] == M_CONSTANT && symtab[a][S_OBJ] == 2) {
        emit(DIV2);
        emit(cg_stack[cg_stack_pos - 1]);
        emit(0);

        cg_stack_pos -= 2; cg_stack_extra += 2;

        cont21d(DIV2, 0, a, 0); /* could have fractional result */
        previous_op = DIV2;
    }
    else
        cont21ii(DIVIDE, 0);
}

void emit_dollar()
{
    int a;

    if (lhs_ptr && current_sequence_pos == 0)
        emit(PLENGTH);
    else
        emit(LENGTH);

    emit(current_sequence[current_sequence_pos]);

    a = new_temp();
    integer_temp(a);

    cg_push(a);
    emit(a);

    previous_op = DOLLAR;
    assignable = FALSE;
}

void emit_endfor(int op)
{
    emit(op); /* will be patched at runtime */
    emit(op_info2); /* address at the top of the loop */
    emit(cg_stack[cg_stack_pos - 1]); /* limit */
    emit(op_info1); /* loop var */
    emit(cg_stack[cg_stack_pos]); /* increment - not always used */

    cg_stack_extra += 2; cg_stack_pos -= 2;

    assignable = FALSE;
    previous_op = op;
}

void emit_floor()
{
    if (previous_op == DIVIDE) {
        BACKPATCH(seg_pos - 3, FLOOR_DIV);
        assignable = TRUE;
        previous_op = FLOOR_DIV;
    }
    else if (previous_op == DIV2) {
        BACKPATCH(seg_pos - 3, FLOOR_DIV2);

        if (is_integer(code_area[code_area_pos][seg_pos - 2]));
            integer_temp(cg_stack[cg_stack_pos]); /* mark the temp as integer type */

        assignable = TRUE;
        previous_op = FLOOR_DIV2;
    }
    else {
        cont11ii(FLOOR, 1);
        previous_op = FLOOR;
    }
}

void emit_for()
{
    int a, b, c, op;

    c = cg_stack[cg_stack_pos]; /* increment */
    keep_temp(c);

    if (symtab[c][S_MODE] == M_NORMAL) {
        if (symtab[c][S_SCOPE] != SC_LOOP_VAR && symtab[c][S_SCOPE] != SC_GLOOP_VAR) {
            emit(ASSIGN);
            emit(c);
            c = new_temp_sym();
            emit(c);
        }
    }

    b = cg_stack[cg_stack_pos - 1]; /* limit */
    keep_temp(b);

    if (symtab[b][S_MODE] == M_NORMAL) {
        if (symtab[b][S_SCOPE] != SC_LOOP_VAR && symtab[b][S_SCOPE] != SC_GLOOP_VAR) {
            emit(ASSIGN);
            emit(b);
            b = new_temp_sym();
            emit(b);
        }
    }

    a = cg_stack[cg_stack_pos - 2]; /* initial value */

    if (is_integer(a) && is_integer(b) && is_integer(c)) {
        symtab[op_info1][S_VTYPE] = INTEGER_TYPE;
        op = FOR_I;
    }
    else
        op = FOR;

    emit(op);
    emit(c);
    emit(b);
    emit(a);
    emit(currentsub); /* in case recursion check is needed */
    cg_stack[cg_stack_pos - 2] = b;
    cg_stack[cg_stack_pos - 1] = c;

    cg_stack_extra++; cg_stack_pos--;

    assignable = FALSE;
    previous_op = op;
}

void emit_get_key()
{
    int a;

    emit(GET_KEY);
    a = new_temp_sym();
    integer_temp(a);
    cg_push(a);
    emit(a);

    assignable = TRUE;
}

void emit_ifwhile(int op)
{
    int a, end;

    a = cg_stack[cg_stack_pos];

    if (previous_op >= LESS && previous_op <= NOT) {
        seg_extra++; seg_pos--;

        if (previous_op == NOT) {
            op = NOT_IFW;
            BACKPATCH(seg_pos - 1, NOT_IFW);
        }
        else {
            if (is_integer(code_area[code_area_pos][seg_pos]) &&
                 is_integer(code_area[code_area_pos][seg_pos - 1]) )
                op = previous_op + LESS_IFW_I - LESS;
            else
                op = previous_op + LESS_IFW - LESS;

            BACKPATCH(seg_pos - 2, op);
        }
    }
    else {
        if (op == WHILE && symtab[a][S_MODE] == M_CONSTANT &&
            IS_ATOM_INT(symtab[a][S_OBJ]) && symtab[a][S_OBJ])
            optimized_while = TRUE; /* while TRUE ... emit nothing */
        else {
            emit(op);
            emit(a);
        }
    }

    cg_stack_extra++; cg_stack_pos--;

    assignable = FALSE;
    previous_op = op;
}

void emit_integer_check()
{
    if (previous_op == ASSIGN) {
        if (!is_integer(code_area[code_area_pos][seg_pos - 1])) {
            emit(INTEGER_CHECK);
            emit(op_info1);
        }
    }
    else if (previous_op == -1 || op_result[previous_op] != T_INTEGER) {
        emit(INTEGER_CHECK);
        emit(op_info1);
    }

    assignable = FALSE;
    previous_op = INTEGER_CHECK;
}

void emit_lhs_subs(int op)
{
    int a, lhs_var;

    a = cg_stack[cg_stack_pos]; /* subs */
    lhs_var = cg_stack[cg_stack_pos - 1]; /* sequence */

    emit(op);
    emit(lhs_var);
    emit(a);

    if (op == LHS_SUBS) {
        keep_temp(lhs_var); /* should be lhs_target_temp */
        emit(lhs_target_temp);
        emit(0); /* place holder */
    }
    else {
        lhs_target_temp = new_temp_sym(); /* same temp for all subscripts */
        emit(lhs_target_temp); /* target temp holds pointer to sequence */
        lhs_subs1_copy_temp = new_temp_sym(); /* place to copy */
        emit(lhs_subs1_copy_temp);
    }

    cg_stack[cg_stack_pos - 1] = lhs_target_temp;

    cg_stack_extra++; cg_stack_pos--;

    current_sequence_extra--; current_sequence_pos++;
    if (current_sequence_extra == -1) {
        current_sequence = (int *)realloc(current_sequence, (current_sequence_pos+32) *
                           sizeof(int));
        current_sequence_extra = 31;
    }
    current_sequence[current_sequence_pos] = lhs_target_temp;
    /* need to update current_sequence like in RHS_SUBS */

    assignable = FALSE;
    previous_op = op;
}

/* All emit_m... calls handle a group of ops. */

void emit_m1(int op)
{
    emit(op);
    assignable = FALSE;
    previous_op = op;
}

void emit_m2(int op)
{
    emit(op);
    emit(cg_stack[cg_stack_pos - 1]);
    emit(cg_stack[cg_stack_pos]);

    cg_stack_extra += 2; cg_stack_pos -= 2;

    assignable = FALSE;
    previous_op = op;
}

void emit_c_proc()
{
    emit_m2(C_PROC);
    emit(currentsub);
}

void emit_m3(int op)
{
    emit(op);
    emit(cg_stack[cg_stack_pos - 2]);
    emit(cg_stack[cg_stack_pos - 1]);
    emit(cg_stack[cg_stack_pos]);

    cg_stack_extra += 3; cg_stack_pos -= 3;

    assignable = FALSE;
    previous_op = op;
}

void emit_m4(int op)
{
    int a;

    emit(op);
    a = new_temp_sym();
    cg_push(a);
    emit(a);

    assignable = TRUE;
}

void emit_m5(int op)
{
    emit(op);
    emit(cg_stack[cg_stack_pos]);

    cg_stack_extra++; cg_stack_pos--;
    assignable = FALSE;
}

void emit_multiply()
{
    int a, b;

    b = cg_stack[cg_stack_pos];
    a = cg_stack[cg_stack_pos - 1];

    if (symtab[a][S_MODE] == M_CONSTANT && symtab[a][S_OBJ] == 2) {
        cg_stack_extra += 2; cg_stack_pos -= 2;
        emit(PLUS);
        emit(b);
        emit(b);
        cont21d(PLUS, a, b, 0);
        previous_op = PLUS;
    }
    else if (symtab[b][S_MODE] == M_CONSTANT && symtab[b][S_OBJ] == 2) {
        cg_stack_extra += 2; cg_stack_pos -= 2;
        emit(PLUS);
        emit(a);
        emit(a);
        cont21d(PLUS, a, b, 0);
        previous_op = PLUS;
    }
    else
        cont21ii(MULTIPLY, 0);
}

void emit_opsubs(int op)
{
    int a, b, c, d;

    a = cg_stack[cg_stack_pos];     /* rhs value */
    b = cg_stack[cg_stack_pos - 1]; /* subscript */
    c = cg_stack[cg_stack_pos - 2]; /* lhs sequence */

    emit(op);
    emit(c);
    emit(b);
    d = new_temp_sym(); /* place to store result */
    emit(d);

    cg_stack[cg_stack_pos] = d;

    cg_push(a);

    assignable = FALSE;
    previous_op = op;
}

void emit_platform()
{
    int a;

#ifdef LINUX_OS
    a = 3;
#elif WINDOWS_OS
    a = 2;
#endif

    cg_push(new_int_sym(a));
    assignable = FALSE;
}

void emit_plus()
{
    int a, b;

    b = cg_stack[cg_stack_pos];
    a = cg_stack[cg_stack_pos - 1];

    if (symtab[a][S_MODE] == M_CONSTANT && symtab[a][S_OBJ] == 1) {
        cg_stack_extra += 2; cg_stack_pos -= 2;
        emit(PLUS1);
        emit(b);
        emit(0);
        cont21d(PLUS1, a, b, 0);
        previous_op = PLUS1;
    }
    else if (symtab[b][S_MODE] == M_CONSTANT && symtab[b][S_OBJ] == 1) {
        cg_stack_extra += 2; cg_stack_pos -= 2;
        emit(PLUS1);
        emit(a);
        emit(0);
        cont21d(PLUS1, a, b, 0);
        previous_op = PLUS1;
    }
    else
        cont21ii(PLUS, 0);
}

void emit_power()
{
    int a, b;

    a = cg_stack[cg_stack_pos];
    b = cg_stack[cg_stack_pos - 1];

    if (symtab[a][S_MODE] == M_CONSTANT && symtab[a][S_OBJ] == 2) {
        cg_stack_pos -= 2; cg_stack_extra += 2;
        emit(MULTIPLY);
        emit(b);
        emit(b);
        cont21d(MULTIPLY, b, a, 0);
        previous_op = MULTIPLY;
    }
    else
        cont21ii(POWER, 0);
}

void emit_proc()
{
    int a, i, lmax, lmin, n, sym;

    assignable = FALSE;
    sym = op_info1;
    n = symtab[sym][S_NUM_ARGS];

    /* Set start and end points for the loop. */
    lmin = cg_stack_pos - n + 1;
    lmax = cg_stack_pos + 1;

    if (sym == currentsub) {
        for (i = lmin;i < lmax;i++) {
            if (symtab[cg_stack[i]][S_SCOPE] == SC_PRIVATE &&
                symtab[cg_stack[i]][S_VARNUM] < i) {
                emit(ASSIGN);
                emit(cg_stack[i]);
                cg_stack[i] = new_temp_sym();
                emit(cg_stack[i]);
            }
        }
    }

    emit(PROC);
    emit(sym);

    for (i = lmin;i < lmax;i++) {
        emit(cg_stack[i]);
        free_temp(cg_stack[i]);
        if (OpWarning) {
            if (symtab[cg_stack[i]][S_USAGE] == U_VAR_READ)
                symtab[cg_stack[i]][S_USAGE] = U_VAR_RW;
            else if (symtab[cg_stack[i]][S_USAGE] == U_VAR_UNUSED)
                symtab[cg_stack[i]][S_USAGE] = U_VAR_WRITTEN;
        }
    }

    cg_stack_pos -= n; cg_stack_extra += n;

    if (symtab[sym][S_TOKEN] != PROC) {
        assignable = TRUE;
        a = new_temp_sym(); /* Temp to store the result. */
        cg_push(a);
        emit(a);
    }

    previous_op = PROC;
}

void emit_returnf()
{
    emit(RETURNF);
    emit(currentsub);
    emit(cg_stack[cg_stack_pos]);

    cg_stack_extra++; cg_stack_pos--;

    assignable = FALSE;
    previous_op = RETURNF;
}

void emit_returnp()
{
    emit(RETURNP);
    emit(currentsub);

    assignable = FALSE;
    previous_op = RETURNP;
}

void emit_rhs_slice()
{
    int c;

    emit(RHS_SLICE);
    emit(cg_stack[cg_stack_pos - 2]);
    emit(cg_stack[cg_stack_pos - 1]);
    emit(cg_stack[cg_stack_pos]);

    c = new_temp_sym();

    cg_stack[cg_stack_pos - 2] = c;
    cg_stack_extra += 2; cg_stack_pos -= 2;

    emit(c);

    previous_op = RHS_SLICE;
    assignable = TRUE;
}

void emit_rhs_subs()
{
    int a, b, mode, op, target;

    a = cg_stack[cg_stack_pos];     /* subscript */
    b = cg_stack[cg_stack_pos - 1]; /* sequence */

    mode = symtab[b][S_MODE];
    target = new_temp_sym();

    op = RHS_SUBS;

    if (mode == M_NORMAL) {
        if (symtab[b][S_VTYPE] != SEQUENCE_TYPE &&
            symtab[symtab[symtab[b][S_VTYPE]][S_NEXT]][S_VTYPE] != SEQUENCE_TYPE)
            op = RHS_SUBS_CHECK;
    }
    else if (mode != M_CONSTANT || !IS_SEQUENCE(symtab[b][S_OBJ]))
        op = RHS_SUBS_CHECK;

    emit(op);
    emit(b);
    emit(a);
    emit(target);
    cg_stack[cg_stack_pos - 1] = target;

    cg_stack_extra++; cg_stack_pos--;

    current_sequence_extra--; current_sequence_pos++;
    if (current_sequence_extra == -1) {
        current_sequence = (int *)realloc(current_sequence, (current_sequence_pos+32) *
                           sizeof(int));
        current_sequence_extra = 31;
    }
    current_sequence[current_sequence_pos] = target;

    assignable = TRUE;
    previous_op = op;
}

int good_str2(int elements)
{
    int e, i, len;
    int obj, mode;
    object_ptr Base;
    s1_ptr ptr, ptr0;

    ptr0 = SEQ_PTR(elements);
    len = ptr0->length + 1;

    for (i = 1;i < len;i++) {
        e = *(ptr0->base + i);

        // mode != M_CONSTANT or not integer(obj)
        if (symtab[e][S_MODE] != M_CONSTANT || !IS_ATOM_INT(symtab[e][S_OBJ]))
            return -1;
    }

    /* If we're down here, all of the [e][S_OBJ] parts must have been okay.
       Create a blank S1, then fill in reverse order with the S_OBJ parts. */

    int ptr2_;
    object_ptr Base2;

    ptr2_ = NewS1(len-1);
    Base2 = ((s1_ptr)ptr2_)->base;

    for (i = 1;i < len;i++) {
        e = *(ptr0->base + len - i);
        Base2[i] = symtab[e][S_OBJ]; // elements[e][S_OBJ]
    }

    return MAKE_SEQ(ptr2_);
}

void emit_right_brace_n()
{
    int a, elements, end, i, ptr_;
    long element_vals;
    object_ptr Base, point0;
    s1_ptr ptr, ptr0;

    ptr_ = NewS1(op_info1);
    Base = ((s1_ptr)ptr_)->base;
    i = 0;

    while(op_info1 > i) {
        i++;
        Base[i] = cg_stack[cg_stack_pos + 1 - i]; /* Grab all elements from right to left. */
    }

    cg_stack_pos -= i; cg_stack_extra += i;

    elements = MAKE_SEQ(ptr_);
    RefDS(elements);

    element_vals = good_str2(elements);

    if (IS_SEQUENCE(element_vals)) {
        RefDS(element_vals);
        a = new_atom_sym(element_vals); // make a string literal

        assignable = FALSE; // FALSE
    }
    else {
        if (op_info1 == 2)
            emit(RIGHT_BRACE_2); // RIGHT_BRACE_2 /* Faster for two items. */
        else {
            emit(RIGHT_BRACE_N); // RIGHT_BRACE_N
            emit(op_info1);
        }

        i = 0;
        ptr = SEQ_PTR(elements);
        Base = ptr->base;

        while (op_info1 > i) {
            i++;
            emit(*(Base + i));
        }

        a = new_temp_sym();
        emit(a);
        assignable = TRUE; // TRUE
    }

    cg_push(a);
    previous_op = RIGHT_BRACE_N;
}

void emit_routine_id()
{
    int a;

    emit(ROUTINE_ID);
    emit(currentsub);
    emit(st_pos);
    emit(cg_stack[cg_stack_pos]);
    emit(current_file_no); /* necessary at top level */

    a = new_temp_sym();
    integer_temp(a); /* result will always be an integer */

    cg_stack[cg_stack_pos] = a;
    emit(a);

    assignable = TRUE;
}

void emit_sc1andor(int op)
{
    int a;

    emit(op);
    emit(cg_stack[cg_stack_pos]);
    a = new_temp_sym();
    cg_stack[cg_stack_pos] = a;
    emit(a);

    assignable = FALSE;
    previous_op = op;
}

void emit_sc2andor(int op)
{
    emit(op);
    emit(cg_stack[cg_stack_pos]);
    emit(cg_stack[cg_stack_pos - 1]); /* target */

    cg_stack_extra++; cg_stack_pos--;

    assignable = FALSE;
    previous_op = op;
}

void emit_sc2null()
{
    cg_stack[cg_stack_pos - 1] = cg_stack[cg_stack_pos];
    /* Override SC1 temp with last value. */

    cg_stack_extra++; cg_stack_pos--;

    assignable = FALSE;
    previous_op = SC2_NULL;
}

void emit_sequence_check()
{
    int a;

    if (previous_op == ASSIGN) {
        a = code_area[code_area_pos][seg_pos - 1];
        if (symtab[a][S_MODE] != M_CONSTANT || !IS_SEQUENCE(symtab[a][S_OBJ])) {
            emit(SEQUENCE_CHECK);
            emit(op_info1);
        }
    }
    else if (previous_op == -1 || op_result[previous_op] != T_SEQUENCE) {
        emit(SEQUENCE_CHECK);
        emit(op_info1);
    }

    assignable = FALSE;
    previous_op = SEQUENCE_CHECK;
}

void emit_task_self()
{
    int a;

    a = new_temp_sym();
    integer_temp(a);
    cg_push(a);
    emit(TASK_SELF);
    emit(a);
    assignable = TRUE;
    previous_op = TASK_SELF;
}

void emit_trace()
{
    int a;

    a = cg_stack[cg_stack_pos];

    if (OpTrace) {
        emit(TRACE);
        emit(a);
    }

    cg_stack_extra++; cg_stack_pos--;
    assignable = FALSE;
}

void emit_type_check()
{
    int a;

    emit(TYPE_CHECK);

    cg_stack_extra++; cg_stack_pos--;
    assignable = FALSE;
    previous_op = TYPE_CHECK;
}

void emit_uminus()
{
    int a, obj;

    a = cg_stack[cg_stack_pos];
    obj = symtab[a][S_OBJ];

    if (symtab[a][S_MODE] == M_CONSTANT && IS_ATOM_INT(obj)) {
        cg_stack[cg_stack_pos] = new_int_sym(-obj);
    }
    else
        cont11ii(UMINUS, 0);
}

void emit_op(int op)
{
    /* Note : In Gyroe, only predefined routines come through here. */

    switch (op) {
        case TASK_YIELD:
        case TASK_CLOCK_START:
        case TASK_CLOCK_STOP:
        case CLEAR_SCREEN:
            emit_m1(op);
            break;
        case RAND:
        case PEEK:
        case PEEK4S:
        case PEEK4U:
        case NOT_BITS:
        case TASK_STATUS:
            cont11ii(op, 1);
            break;
        case IS_AN_INTEGER:
        case IS_AN_ATOM:
        case IS_A_SEQUENCE:
        case IS_AN_OBJECT:
        case LENGTH:
        case GETC:
        case SQRT:
        case SIN:
        case COS:
        case TAN:
        case ARCTAN:
        case LOG:
        case GETS:
        case GET_PIXEL:
        case GETENV:
            cont11ii(op, 0);
            break;
        case ROUTINE_ID:
            emit_routine_id();
            break;
        case SYSTEM:
        case PUTS:
        case PRINT:
        case POSITION:
        case MACHINE_PROC:
        case PIXEL:
        case POKE:
        case POKE4:
        case TASK_SCHEDULE:
        case CALL_PROC:
            emit_m2(op);
            break;
        case C_FUNC:
            emit_c_func();
            break;
        case C_PROC:
            emit_c_proc();
            break;
        case APPEND:
        case PREPEND:
        case COMPARE:
        case EQUAL:
        case FIND:
        case MATCH:
        case SYSTEM_EXEC:
        case REPEAT:
        case MACHINE_FUNC:
        case OPEN:
        case SPRINTF:
        case TASK_CREATE:
            cont21ii(op, 0);
            break;
        case MEM_COPY:
        case MEM_SET:
        case PRINTF:
            emit_m3(op);
            break;
        case CALL_FUNC:
            emit_call_func();
            break;
        case DATE:
        case TIME:
        case TASK_LIST:
        case COMMAND_LINE:
            emit_m4(op);
            break;
        case REMAINDER:
        case AND_BITS:
        case OR_BITS:
        case XOR_BITS:
            cont21ii(op, 1);
            break;
        case GET_KEY:
            emit_get_key();
            break;
        case FLOOR:
            emit_floor();
            break;
        case POWER:
            emit_power();
            break;
        case PLATFORM:
            emit_platform();
            break;
        case CLOSE:
        case ABORT:
        case CALL:
        case PROFILE:
        case TASK_SUSPEND:
            emit_m5(op);
            break;
        case TRACE:
            emit_trace();
            break;
        case TASK_SELF:
            emit_task_self();
            break;
        default:
            internal_error("Unknown opcode");
            break;
    }

    previous_op = op;
}

void emit_assign_op(int op)
{
    switch(op) {
        case PLUS_EQUALS:
            emit_plus();
            break;
        case MINUS_EQUALS:
            cont21ii(MINUS, 0);
            break;
        case MULTIPLY_EQUALS:
            emit_multiply();
            break;
        case DIVIDE_EQUALS:
            emit_divide();
            break;
        case CONCAT_EQUALS:
            cont21ii(CONCAT, 0);
            break;
    }
}

void start_source_line(int sl)
{
    last_line_number = gline_number;

    if (sl && OpTrace || OpProfileStatement) {
        emit_m1(STARTLINE);
        emit(gline_number);
    }
}

